#include <iostream>
#include "SQLParser.h"
#include "SQLCommon.h"
using namespace std;

SQLParser::SQLParser(void)
{
}

SQLParser::~SQLParser(void)
{
}

int SQLParser::FindString(string src, string p)
{
	size_t curr = 0;
	while (curr != src.size())
	{
		bool match = true;
		curr = src.find_first_of(p.at(0), curr);
		if (curr == -1) return -1;
		if (src.size() - curr < p.size()) return -1;
		for (size_t i = 0; i < p.size(); i++){
			if (src.at(curr + i)!= p.at(i)){
				match = false;
				break;
			}
		}
		if (match) return (int)curr;
		curr ++;
	}
	return -1;
}

vector<string> SQLParser::DivideByString(string src, string div)
{
	vector <string> divString;
	int curr = FindString(src, div);
	while (curr != -1)
	{
		divString.push_back(src.substr(0, curr));
		src = src.substr(curr + div.size(), src.size() - curr - div.size());
		curr = FindString(src, div);
	}
	divString.push_back(src);
	return divString;
}
string SQLParser::DeWhiteSpace(string target){
	string space = " \t\r\n";
	size_t curr = target.find_first_of(space);
	while (curr != -1)
	{
		target.erase(curr, 1);
		curr = target.find_first_of(space);
	}
	return target;
}

int SQLParser::Segmentation()
{
	vector <int> vecOffset;
	vector <int> vecType;

	int offset = 0;

	for (int i = 0; i < 10; i++)
	{ 
		offset = FindString(sqlQuery, QUERY_TITLE[i]);
		if (offset == -1)
			offset = FindString(sqlQuery, QUERY_TITLE[i + 10]);
		if (offset != -1) {
			vecOffset.push_back(offset);
			vecType.push_back(i);
		}
	}

	if (vecOffset.empty()) 
	{
		vecType.push_back(-1);
		return -1;
	}

	while (vecOffset.size() != 0)
	{
		size_t beg = vecOffset.back();
		sqlSeg.push_back(sqlQuery.substr(beg, sqlQuery.size() - beg) );
		vecOffset.pop_back();

		sqlSegType.push_back(vecType.back() + 1);
		vecType.pop_back();

		if (vecOffset.size() != 0) 
			sqlQuery = sqlQuery.substr(0, beg - 1);
	}
	return 0;
}

int SQLParser::QueryCreateTable()
{
	//printf("query create\n");
	string strCreate = sqlSeg.back();
	strCreate.erase(0, 12);

	queryInfo->table_name = GetTables(strCreate.substr(0, strCreate.find_first_of("(")));

	strCreate.erase(0, strCreate.find_first_of("(") + 1);
	strCreate = strCreate.substr(0, strCreate.find_last_of(")") - 1);

	vector<string> vecCreate = DivideByString(strCreate, ",");

	for (size_t i = 0; i < vecCreate.size(); i++){

		ColInfo cInfo;
		string col = vecCreate.at(i);
		size_t beg = col.find_first_of("[") + 1;
		cInfo.col_name = col.substr(beg, col.find_first_of("]") - beg);

		beg = col.find_first_of("(") + 1;
		cInfo.data_len = 0;
		string len = col.substr(beg, col.find_last_of(")") - beg);
		if (FindString(col, "int") == -1){
			if (len.find_first_not_of("0123456789") != -1) return -1;
			while (len.size() != 0){
				cInfo.data_len = (int)len.at(0) - 48 + cInfo.data_len * 10;
				len.erase(0, 1);
			}
		}
		if (FindString(col, "int") != -1) cInfo.data_type = 1;
		else if (FindString(col, "varchar") != -1) cInfo.data_type = 3;
		else if (FindString(col, "char") != -1) cInfo.data_type = 2;

		if (FindString(col, "NOT NULL") != -1) cInfo.nullable = false;
		else if (FindString(col, "NULL") != -1) cInfo.nullable = true;

		queryInfo->col_name.push_back(cInfo);
	}


	return 0;
}

int SQLParser::QueryDropTable()
{
	//printf("query drop\n");
	string strDrop = sqlSeg.back();
	strDrop.erase(0, 10);
	queryInfo->table_name = GetTables(strDrop);
	return 0;
}

int SQLParser::QueryInsertInto()
{
	//printf("query insert\n");
	string strInsert = sqlSeg.back();
	strInsert.erase(0, 11);
	size_t beg;
	vector<string> vecColumns;
	if ((beg = strInsert.find_first_of("(")) != -1){
		string tmpstr = strInsert.substr(beg + 1, strInsert.find_last_of(")") - beg - 1);
		vecColumns = DivideByString(tmpstr, ",");
		strInsert.erase(beg, strInsert.size() - beg);
	}
	queryInfo->table_name = GetTables(strInsert);
	
	string strValues = sqlSeg.front();
	strValues.erase(0, 6);
	beg = strValues.find_first_of("(");
	strValues = strValues.substr(beg + 1, strValues.find_last_of(")") - beg - 1);
	vector<string> vecValues = DivideByString(strValues, ",");

	if (vecValues.size() == vecColumns.size() || vecColumns.size() == 0){
		for (size_t i = 0; i < vecValues.size(); i++){
			ConditionInfo cInfo;
			if (vecColumns.size() != 0)
				cInfo.col1_name = vecColumns.at(i);
			cInfo.op = 1;
			size_t begin;
			string value = vecValues.at(i);
			if ((begin = value.find_first_of("'")) != -1)
				cInfo.value = value.substr(begin + 1, value.find_last_of("'") - begin - 1);
			else if (DeWhiteSpace(value).size() == 0 || DeWhiteSpace(value).compare("NULL") == 0)
				cInfo.value = "\f";
			else cInfo.value = value; 
			queryInfo->conditions.push_back(cInfo);
		}
	}


	//string strInsert = sqlSeg.back();
	//strInsert.erase(0, 11);
	//queryInfo->table_name = GetTables(strInsert);

	return 0;
}

int SQLParser::QueryDeleteFrom()
{
	//printf("query delete\n");
	sqlSeg.pop_back();
	string strFrom = sqlSeg.back();
	strFrom.erase(0, 4);
	queryInfo->table_name = GetTables(strFrom);

	sqlSeg.pop_back();
	string strWhere = sqlSeg.back();
	strWhere.erase(0, 5);
	queryInfo->conditions = GetConditions(strWhere, 1);
	return 0;
}

int SQLParser::QueryUpdate()
{
	//printf("query update\n");
	string strUpdate = sqlSeg.back();
	strUpdate.erase(0, 6);
	queryInfo->table_name = GetTables(strUpdate);

	sqlSeg.pop_back();
	string strSet = sqlSeg.back();
	strSet.erase(0, 3);
	queryInfo->conditions = GetConditions(strSet, 2);

	sqlSeg.pop_back();
	string strWhere = sqlSeg.back();
	strWhere.erase(0, 5);
	vector<ConditionInfo> tmpInfo = GetConditions(strWhere, 1);
	for (size_t i = 0; i < tmpInfo.size(); i++)
		queryInfo->conditions.push_back(tmpInfo.at(i));
	return 0;
}

int SQLParser::QuerySelect()
{
	//printf("query select\n");
	string strSelect = sqlSeg.back();
	strSelect.erase(0, 6);
	if (DeWhiteSpace(strSelect).compare("*") != 0)
		queryInfo->col_name = GetColumns(strSelect);
		
	sqlSeg.pop_back();
	string strFrom = sqlSeg.back();
	strFrom.erase(0, 4);
	queryInfo->table_name = GetTables(strFrom);

	if (sqlSeg.size() == 2){
		sqlSeg.pop_back();
		string strWhere = sqlSeg.back();
		strWhere.erase(0, 5);
		queryInfo->conditions = GetConditions(strWhere, 1);
	}
	return 0;
}

vector<ColInfo> SQLParser::GetColumns(string src)
{
	vector<string> tempColumns = DivideByString(src, ",");
	vector<ColInfo> columns;

	for (size_t i = 0; i < tempColumns.size(); i++)
	{
		vector<string> tmp = DivideByString(DeWhiteSpace(tempColumns.at(i)), ".");
		ColInfo cInfo;
		cInfo.col_name = tmp.back();
		if (tmp.size() == 2)
			cInfo.table_name = tmp.front();
		columns.push_back(cInfo);
	}
	return columns;
}

vector<string> SQLParser::GetTables(string src)
{
	vector<string> tempTables = DivideByString(src, ",");
	vector<string> tables;

	for (size_t i = 0; i < tempTables.size(); i++){
		string tmp = DeWhiteSpace(tempTables.at(i));
		tables.push_back(tmp);
	}
	return tables;
}

vector<ConditionInfo> SQLParser::GetConditions(string src, int scope){
	
	string logicCombination;
	int logicType;
	if (FindString(src, "AND") != -1) {
		logicCombination = " AND "; 
		logicType = 1;
	}else if (FindString(src, "OR") != -1){
		logicCombination = " OR ";
		logicType = 2;
	}else {
		logicCombination = " AND ";
		logicType = 0;
	}
	queryInfo->logic_type = logicType;

	vector<string> tempConditions = DivideByString(src, logicCombination);
	vector<ConditionInfo> conditions;
	for (size_t i = 0; i < tempConditions.size(); i++){
		string tmp = tempConditions.at(i);
		int opType = 0, off = 0;
		while ((off = FindString(tmp, OP[opType])) == -1){
			opType ++;
		}
		ConditionInfo cInfo;
		cInfo.scope = scope;
		cInfo.op = opType + 1;

		vector<string> exp1 = DivideByString(DeWhiteSpace(tmp.substr(0, off)), ".");
		cInfo.col1_name = exp1.back();
		if (exp1.size() == 2)
			cInfo.table1_name = exp1.front();

		size_t offsetExp;
		////same as above
		//if ((offsetExp = exp1.find_first_of(".")) != -1){
		//	cInfo.table1_name = DeWhiteSpace(exp1.substr(0, offsetExp));
		//	cInfo.col1_name = DeWhiteSpace(exp1.substr(offsetExp+1, exp1.size()));
		//}
		//else 
		//	cInfo.col1_name = DeWhiteSpace(exp1);

		if (cInfo.op <=6 || cInfo.op > 9 )
		{
			string exp2 = tmp.substr(off + 1, tmp.size());
			if (scope == 2 && (FindString(exp2, "NULL") != -1))//update set a = NULL;
				cInfo.value = "\f";
			else if((offsetExp = exp2.find_first_of("'")) != -1)
				cInfo.value = exp2.substr(offsetExp + 1, exp2.find_last_of("'") - offsetExp - 1);
			else if ((offsetExp = exp2.find_first_of(".")) != -1)
			{
				cInfo.table2_name = DeWhiteSpace(exp2.substr(0, offsetExp));
				cInfo.col2_name = DeWhiteSpace(exp2.substr(offsetExp+1, exp2.size()));
			}
			else 
				cInfo.col2_name = DeWhiteSpace(exp2);

		}
		conditions.push_back(cInfo);
	}
	return conditions;
}

QueryInfo SQLParser::InitQuery(string sqlQuery)
{
	string succ = "success!";

	string err0 = "query should be consist of keywords, see SQL STANDARDS";

	string err1 = "unexpected keywords in query CREATE TABLE";

	string err2 = "unexpected keywords in query DROP TABLE";

	string err31 = "keyword mismatch in query INSERT INTO, should be followed by VALUES";
	string err32 = "unexpected keywords in query INSERT INTO";

	string err41 = "keyword mismatch in query DELETE FROM, should be followed by WHERE";
	string err42 = "unexpected keywords in query DELETE FROM";
	
	string err51 = "keyword mismatch in query UPDATE, should be followed by SET and WHERE";
	string err52 = "unexpected keywords in query UPDATE";
	
	string err61 = "keyword mismatch in query SELECT, should be followed by FROM";
	string err62 = "unexpected keywords in query SELECT";
	
	SQLParser::sqlQuery = sqlQuery;
	queryInfo = new struct QueryInfo;
	sqlSeg.clear();
	sqlSegType.clear();

	Segmentation();
	if (sqlSegType.size() == 0)
		queryInfo->type = -1;
	else 
		queryInfo->type = sqlSegType.back();
	switch(queryInfo->type)
	{
	case -1:
		queryInfo->type = -1;
		queryInfo->err = err0;
		break;
	case 1:{
		sqlSegType.pop_back();
		if (sqlSegType.size() != 0){
			queryInfo->type = -1;
			queryInfo->err = err1;
			break;
		}
		QueryCreateTable();
		break;
		   }
	case 2:
		sqlSegType.pop_back();
		if (sqlSegType.size() != 0){
			queryInfo->type = -1;
			queryInfo->err = err2;
			break;
		}
		QueryDropTable();
		break;
	case 3:
		sqlSegType.pop_back();
		if (sqlSegType.size() == 0){
			queryInfo->type = -1;
			queryInfo->err = err31;
			break;
		}
		if (sqlSegType.back() != 7){
			queryInfo->type = -1;
			queryInfo->err = err31;
			break;
		}
		sqlSegType.pop_back();
		if (sqlSegType.size() != 0){
			queryInfo->type = -1;
			queryInfo->err = err32;
			break;
		}
		QueryInsertInto();
		break;
	case 4:
		for (size_t i = 0; i < sqlSeg.size(); i++){
			printf("%s\n", sqlSeg.at(i).c_str());
		}
		if (sqlSegType.size() < 3){
			queryInfo->type = -1;
			queryInfo->err = err41;
			break;
		}
		if (sqlSegType.at(1) != 9 || sqlSegType.at(0) != 10){
			queryInfo->type = -1;
			queryInfo->err = err41;
			break;
		}
		if (sqlSegType.size() > 3){
			queryInfo->type = -1;
			queryInfo->err = err42;
			break;
		}
		QueryDeleteFrom();
		break;
	case 5:
		if (sqlSegType.size() < 2){
			queryInfo->type = -1;
			queryInfo->err = err51;
			break;
		}
		if (sqlSegType.size() == 2 && sqlSegType.at(0) != 8){
			queryInfo->type = -1;
			queryInfo->err = err51;
			break;
		}
		if (sqlSegType.size() == 3 && (sqlSegType.at(1) != 8 || sqlSegType.at(0) != 10)){
			queryInfo->type = -1;
			queryInfo->err = err51;
			break;
		}
		if (sqlSegType.size() > 3){
			queryInfo->type = -1;
			queryInfo->err = err52;
			break;
		}
		QueryUpdate();
		break;
	case 6:
		if (sqlSegType.size() < 2){
			queryInfo->type = -1;
			queryInfo->err = err61;
			break;
		}
		if (sqlSegType.size() == 2 && sqlSegType.at(0) != 9){
			queryInfo->type = -1;
			queryInfo->err = err61;
			break;
		}
		if (sqlSegType.size() == 3 && (sqlSegType.at(1) != 9 || sqlSegType.at(0) != 10)){
			queryInfo->type = -1;
			queryInfo->err = err61;
			break;
		}
		if (sqlSegType.size() > 3){
			queryInfo->type = -1;
			queryInfo->err = err62;
			break;
		}
		QuerySelect();
		break;
	default:
		queryInfo->type = -1;
		queryInfo->err = err0;
		break;
	}
		
	return *queryInfo;
}


QueryInfo SQLParser::FillInfo(QueryInfo sql)
{
	SQLCheckError* checkError = new SQLCheckError(sql);
	return checkError->CheckError();
}

void SQLParser::PrintQueryInfo(){
	cout<<"query type:"<<queryInfo->type<<"\n";
	cout<<"logic type:"<<queryInfo->logic_type<<"\n";
	for (size_t i = 0; i < queryInfo->table_name.size(); i++)
		cout<<"table"<<i<<": "<<queryInfo->table_name.at(i)<<"\n";
	for (size_t i = 0; i < queryInfo->col_name.size(); i++){
		cout<<"column"<<i<<"\n";
		cout<<"\t table:"<<queryInfo->col_name.at(i).table_name<<"\n";
		cout<<"\t name:"<< queryInfo->col_name.at(i).col_name<<"\n";
		cout<<"\t type:"<<queryInfo->col_name.at(i).data_type<<"\n";
		cout<<"\t len:"<< queryInfo->col_name.at(i).data_len<<"\n";
		cout<<"\t nullable:"<< queryInfo->col_name.at(i).nullable<<"\n";
	}
	for (size_t i = 0; i < queryInfo->conditions.size(); i++){
		cout<<"condition"<<i<<"\n";
		cout<<"\t scope: "<<queryInfo->conditions.at(i).scope<<"\n";
		cout<<"\t table1: "<<queryInfo->conditions.at(i).table1_name<<"\n";
		cout<<"\t col1: "<<queryInfo->conditions.at(i).col1_name<<"\n";
		cout<<"\t table2: "<<queryInfo->conditions.at(i).table2_name<<"\n";
		cout<<"\t col2: "<<queryInfo->conditions.at(i).col2_name<<"\n";
		cout<<"\t value: "<<queryInfo->conditions.at(i).value<<"\n";
		cout<<"\t op: "<<queryInfo->conditions.at(i).op<<"\n";
	}
	return;
}


